
`include "common_header.verilog"

//  *************************************************************************
//  File : r_fifo_rena_store
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2010 Morethanip GmbH, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Daniel Koehler
//  info@morethanip.com
//  *************************************************************************
//  Description: Storage of Memory output to support clock enable using a
//               normal memory which does not support clock enable on its address bus.
//               The module is used on the transmit FIFO read side.
//               Memory output data gets registered here if delay is 1-cycle. When it is
//               2-cycles the memory output is driven directly into dout. There is also a
//               2:1 mux at the output
//  Version    : $Id: r_fifo_rena_store.v,v 1.1 2011/05/17 10:08:50 dk Exp $
//  *************************************************************************

module r_fifo_rena_store (

   reset_rclk,          // memory read domain
   rclk,                // read clock
   rclk_ena,            // read clock enable
   rden,                // read enable to the memory
   dout_mem,            // data coming from memory, one cycle after rden asserted
   dout,                // output data, latched
   dmem_val);           // dout_mem valid indication (may be one before dout, to allow other external latching if needed)

parameter FF_WIDTH      = 32;
parameter MEM_DELAY2    = 0;    // Set to 1 if the external memory has a read delay of 2 cycles, otherwise 1 cycle read delay is used

input                   reset_rclk;
input                   rclk;
input                   rclk_ena;
input                   rden;
input  [FF_WIDTH - 1:0] dout_mem;
output [FF_WIDTH - 1:0] dout;
output                  dmem_val;

wire    [FF_WIDTH - 1:0] dout;
wire    dmem_val;

// local

reg                dval;              // data valid
reg                dout_sel_prev;       // output data selector
reg [FF_WIDTH-1:0] dout_prev;           // pipeline with previous data

generate
if (MEM_DELAY2 == 0)
begin : g_mem_dly1

    reg [FF_WIDTH-1:0] dout_l;              // latched memory output data

    // capture&hold memory read output
    // -------------------------------

    always@( posedge reset_rclk or posedge rclk )
    begin:p_mem_dly1
            if( reset_rclk==1'b 1 )
            begin
                    dval            <= 1'b 0;
                    dout_l          <= {(FF_WIDTH){1'b 0}};
                    dout_sel_prev   <= 1'b 0;
                    dout_prev       <= {(FF_WIDTH){1'b 0}};
            end
            else
            begin
                    // maintain a latched memory read value when clock enable toggles
                    dval <= rclk_ena & rden;       // mark valid data the cycle following an enabled read

                    if( dval==1'b 1 )
                    begin
                            dout_l <= dout_mem;  // capture memory read data
                    end

                    if( dval==1'b 1 & rclk_ena==1'b 0 )
                    begin
                            // a read occured, but the data (one later now) is falling into a ena=low phase,
                            // hence the read data is not yet processed immediately. So must save it and keep
                            // presenting it to the output until the next clock ena is occuring.
                            dout_sel_prev   <= 1'b 1;       // and mark prev as valid
                            dout_prev <= dout_l;    // keep the previous
                    end
                    else if( rclk_ena==1'b 1 )
                    begin
                            // whenever a stored data is processed switch back to normal.
                            dout_sel_prev <= 1'b 0;
                            //dout_prev <= dout_l;    // no need to reload dout_prev register here
                    end
            end
    end


    assign dout     = (dout_sel_prev==1'b 1) ? dout_prev : dout_l;
    assign dmem_val = dval;       // indicate when the dout_mem is valid (not the dout!)

end

else
begin : g_mem_dly2
    // Implement a hold-register for dout_mem. The 1-cycle delay does it with dout_prev, but since here
    // we deal with a 2-cycle delay RAM, we need up to two holding registers since we can't be sure that
    // the external memory will hold its output when rden is low
    reg                 dval_d2;             // data valid, 2 cycles delayed
    reg                 dout_mem_hold_val;   // Indicates that dout_mem_hold contains valid data
    reg  [FF_WIDTH-1:0] dout_mem_hold;       // hold register for dout_mem_hold
    wire [FF_WIDTH-1:0] dout_mem_mux;        // muxed dout_mem and dout_mem_hold based on dout_mem_hold_val

    // hold register for memory data
    // -------------------------------
    always@( posedge reset_rclk or posedge rclk )
    begin:p_dout_mem_hold
            if( reset_rclk==1'b 1 )
            begin
                    dout_sel_prev     <= 1'b 0;
                    dval              <= 1'b 0;
                    dval_d2           <= 1'b 0;
                    dout_mem_hold_val <= 1'b 0;
                    dout_mem_hold     <= {(FF_WIDTH){1'b 0}};
                    dout_prev         <= {(FF_WIDTH){1'b 0}};
            end
            else
            begin
                    // maintain a latched memory read value when clock enable toggles
                    dval    <= rclk_ena & rden;  // mark valid data the cycle following an enabled read
                    dval_d2 <= dval;             // the real valid is then 2 cycles later

                    // -- memory output hold register --
                    if (dout_sel_prev == 1'b 1 && dval_d2 == 1'b 1)// && rclk_ena == 1'b 0)
                    begin
                            // dout_sel_prev is 1 with rclk_ena == 1'b0 means that dout_prev contains held data and is not being read. 
                            // If dval is 1 then we need to latch dout_mem in another register (dout_mem_hold) so it is not lost
                            dout_mem_hold_val <= 1'b 1;
                    end
                    else if (dout_mem_hold_val == 1'b 1 && dout_sel_prev == 1'b 0)
                    begin
                            // Data in dout_prev was read the previous cycle, so this cycle data from dout_mem_hold is being read.
                            dout_mem_hold_val <= 1'b 0;
                    end

                    // Load dout_mem_hold. No need to load it if dval == 1'b1 so this saves power
                    if (dval_d2 == 1'b 1 && dval == 1'b 0)
                    begin
                        dout_mem_hold <= dout_mem;
                    end

                    // -- dout hold register ---
                    // When dval_d2 is 1 and rclk_ena is 0, set dout_sel_prev to hold the previous data into dout
                    if( (dval_d2==1'b 1 || dout_mem_hold_val==1'b 1) && dout_sel_prev == 1'b 0 && rclk_ena==1'b 0 )
                    begin
                            // a read occured, but the data (one later now) is falling into a ena=low phase,
                            // hence the read data is not yet processed immediately. So must save it and keep
                            // presenting it to the output until the next clock ena is occuring.
                            dout_sel_prev   <= 1'b 1;         // and mark prev as valid
                            dout_prev       <= dout_mem_mux;  // keep the previous, memory module already provides the stored output
                    end
                    else if ( dval == 1'b 1 && rclk_ena == 1'b 0)
                    begin
                            // Store the current data output since rclk_ena is 0 but there is a read in the pipeline which would otherwise
                            // overwrite the output in the next cycle if not stored in dout_prev
                            dout_sel_prev   <= 1'b 1;
                            dout_prev       <= dout;
                    end
                    else if( rclk_ena==1'b 1 )
                    begin
                            // whenever a stored data is processed switch back to normal.
                            dout_sel_prev <= 1'b 0;
                            // dout_prev  <= dout_mem;  // no need to reload this register. Comment out to save power
                    end

            end
    end

    // Mux dout_mem_hold with dout_mem. dout_mem_hold is used to store overflowing data due to rclk_en going low
    // during back to back reads from the memory
    // assign dout_mem_mux = (dout_mem_hold_val == 1'b 0) ? dout_mem : dout_mem_hold;
    assign dout_mem_mux = (dval_d2 == 1'b 1) ? dout_mem : dout_mem_hold;

    // Mux the final output. dout_prev is used to hold the output when rclk_ena is 0
    assign dout     = (dout_sel_prev==1'b 1) ? dout_prev : dout_mem_mux;
    assign dmem_val = dval_d2;     // indicate when the dout_mem is valid (not the dout!)

end
endgenerate

endmodule
